package com.zzedu.common.utils;

import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import org.springframework.cglib.core.ReflectUtils;

import java.beans.PropertyDescriptor;
import java.lang.invoke.*;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * 动态创建 Lambda方法
 */
public class LambdaUtil {
    public static final Map<Class<?>, PropertyDescriptor[]> cache = new HashMap<>();

    public static <T> SFunction<T, ?> getLambdaGetter(Class<T> clazz, String prop) throws Throwable {
        PropertyDescriptor[] beanGetters;
        if (cache.containsKey(clazz)) {
            beanGetters = cache.get(clazz);
        } else {
            beanGetters = ReflectUtils.getBeanGetters(clazz);
            cache.put(clazz, beanGetters);
        }
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        Optional<PropertyDescriptor> optional = Arrays.stream(beanGetters)
                .filter(pd -> pd.getName().equals(prop))
                .findFirst();
        if (optional.isPresent()) {
            // 反射获取getter方法
            Method readMethod = optional.get().getReadMethod();
            // 拿到方法句柄
            final MethodHandle methodHandle = lookup.unreflect(readMethod);
            // 创建动态调用链
            CallSite callSite = LambdaMetafactory.altMetafactory(
                    lookup,
                    "apply",
                    MethodType.methodType(SFunction.class),
                    MethodType.methodType(Object.class, Object.class),
                    methodHandle,
                    MethodType.methodType(readMethod.getReturnType(), clazz),
                    LambdaMetafactory.FLAG_SERIALIZABLE
            );
            return (SFunction<T, ?>) callSite.getTarget().invokeExact();
        }
        return null;
    }
}
